package br.com.verde.exemploteca.ivpiter

class ConverterScala {
  private val romanToArabicMap = Map('I' -> 1, 'V' -> 5, 'X' -> 10, 'L' -> 50, 'C' -> 100, 'D' -> 500, 'M' -> 1000, '$' -> 0)

  private def romanToArabicImperative(romanNumber: String) = {
    var arabic = 0
    var previewArabic = 0
    var currentArabic = -1
    var firstIteraction = true;

    for (i <- (0 to romanNumber.length() - 1).reverse) {
      currentArabic = oneRomanCharacterToArabic(romanNumber.charAt(i));
      if (firstIteraction) {
        previewArabic = currentArabic;
        arabic = currentArabic;
        firstIteraction = false;
      } else {
        if (currentArabic < previewArabic) {
          arabic = arabic - currentArabic;
        } else {
          arabic = arabic + currentArabic;
        }
        previewArabic = currentArabic;
      }
    }

    arabic;
  }

  private def oneRomanCharacterToArabic(roman: Char) = roman.toUpper match {
    case 'I' => 1
    case 'V' => 5
    case 'X' => 10
    case 'L' => 50
    case 'C' => 100
    case 'D' => 500
    case 'M' => 1000
    case _ => 0
  }

  private def romanToArabicTailRecursion(roman: String, accumulator: Int, currIndex: Int): Int = {
    val currentArabic = romanToArabicMap(roman.charAt(currIndex))

    if (roman.length == 1) {
      currentArabic
    } else {
      if (accumulator == 0) {
        romanToArabicTailRecursion(roman, currentArabic, currIndex - 1)
      } else {
        val frontArabic = romanToArabicMap(roman.charAt(currIndex + 1))
        val signilizer =
          if (frontArabic > currentArabic)
            -1 else 1

        if (currIndex == 0) {
          accumulator + (currentArabic * signilizer)
        } else {
          romanToArabicTailRecursion(roman, accumulator + (currentArabic * signilizer), currIndex - 1)
        }
      }

    }
  }

  def romanToArabicCombinatorsÉdson(roman: String): Int = {
    println("romanToArabicCombinatorsÉdson")
    if (roman.length() == 0) {
      throw new IllegalArgumentException("Empty roman")
      return 0
    }
    val romanString = roman.toUpperCase
    if (roman.length() == 1) return romanToArabicMap(romanString.toUpperCase.charAt(0))

    val originalVector = romanString.map(romanToArabicMap(_))
    val negativeParts = for {
      Vector(left, right) <- originalVector.sliding(2)
    } yield {
      if (left < right) 2 * (left) else 0
    }
    originalVector.reduceLeft(_ + _) - negativeParts.reduceLeft(_ + _)
  }

  def romanToArabicCombinatorsLeonardo(roman: String): Option[Int] = {
    println("romanToArabicCombinatorsLeonardo")
    val (romanSet, _) = romanToArabicMap unzip

    roman.toUpperCase.toList match {
      case List() => None
      case xs if ((xs.toSet -- romanSet.toSet).size > 0) => None
      case List(d) => Some(romanToArabicMap(d))
      case xs => {
        val original = xs map romanToArabicMap
        val negative =
          for {
            List(left, right) <- (original sliding 2)
            if (left < right)
          } yield (2 * left)
        Some(original.foldLeft(0)(_ + _) - negative.foldLeft(0)(_ + _))
      }
    }
  }

  def romanToArabicCombinatorsThomas(roman: String): Int = {
    println("romanToArabicCombinatorsThomas")
    val upDown = (roman + "$$").map(romanToArabicMap).sliding(2).partition(x => x(0) >= x(1))
    upDown._1.foldLeft(0)(_ + _(0)) - upDown._2.foldLeft(0)(_ + _(0))
  }

  def romanToArabicCombinatorsDaniel(roman: String) = {
    println("romanToArabicCombinatorsDaniel")
    val digits = roman map romanToArabicMap
    digits.tail.foldLeft(digits.head -> List(digits.head)) {
      case ((left, acc :: rest), right) => right -> (
        if (left == right) acc + right :: rest
        else if (left > right) right :: acc :: rest
        else right :: (-acc) :: rest)
    }._2.sum
  }

  def romanToArabicMapReduce(roman: String) = {
    roman.toUpperCase map (romanToArabicMap(_)) reduceRight (_ + _)
  }

  def romanToArabic(roman: String) = {
    romanToArabicCombinatorsDaniel(roman.toUpperCase)
  }
}

object CoverterScala {
  def main(args: Array[String]) {
    val converter = new ConverterScala
    println("mcmlxxix = " + converter.romanToArabic("mcmlxxix"))
    println("ix = " + converter.romanToArabic("ix"))
    println("ii = " + converter.romanToArabic("ii"))
    println("i = " + converter.romanToArabic("i"))
    println("xi = " + converter.romanToArabic("xi"))
    println("MCML = " + converter.romanToArabic("MCML"))
    println("CMXCIX = " + converter.romanToArabic("CMXCIX"))
    println("CDXC = " + converter.romanToArabic("CDXC"))
  }
}